<?php

namespace Muyuxuanpay\wxpay;

class WxpayV3
{
    const KEY_LENGTH_BYTE = 32;
    const AUTH_TAG_LENGTH_BYTE = 16;
    protected static $config;
    public static function setConfig($config=[])
    {
        if(!empty($config)){
            self::$config = $config;
        }
    }

    //获取随机字符串
    public static function getNonceStr()
    {
        $strs = "QWERTYUIOPASDFGHJKLZXCVBNM1234567890";
        $name = substr(str_shuffle($strs), mt_rand(0, strlen($strs) - 11), 32);
        return $name;
    }

    //微信支付签名
    public static function getSign($data = "", $url, $randstr, $time,$method="POST")
    {
        if(empty($data)){
            $str = "{$method}" . "\n" . $url . "\n" . $time . "\n" . $randstr . "\n". "" . "\n";
        }else{
            $str = "{$method}" . "\n" . $url . "\n" . $time . "\n" . $randstr . "\n" . $data . "\n";
        }
        if(!empty(self::$config['private_key'])){
            $key = self::$config['private_key'];
        }elseif (!empty(self::$config['private_key_path'])){
            $key = file_get_contents(ROOT_PATH .'public/'. str_replace('/', DS, substr(self::$config['private_key_path'], 1)));
        }else{
            $key = file_get_contents(ROOT_PATH .'public/'. str_replace('/', DS, substr('/application/certs/apiclient_key.pem', 1)));
        }
        $str = self::getSha256WithRSA($str, $key);
        return $str;
    }

    //调起支付的签名
    public static function getWechartSign($appid, $timeStamp, $noncestr, $prepay_id)
    {
        $str = $appid . "\n" . $timeStamp . "\n" . $noncestr . "\n" . $prepay_id . "\n";
        if(!empty(self::$config['private_key'])){
            $key = self::$config['private_key'];
        }elseif (!empty(self::$config['private_key_path'])){
            $key = file_get_contents(ROOT_PATH .'public/' . str_replace('/', DS, substr(self::$config['private_key_path'], 1)));
        }else{
            $key = file_get_contents(ROOT_PATH . str_replace('/', DS, substr('/application/certs/apiclient_key.pem', 1)));
        }
//        $key = file_get_contents(ROOT_PATH . str_replace('/', DS, substr('/application/certs/apiclient_key.pem', 1))); //在商户平台下载的秘钥,读取到变量
        $str = self::getSha256WithRSA($str, $key);
        return $str;
    }

    //加密
    public static function getSha256WithRSA($content, $privateKey)
    {
        $binary_signature = "";
        $algo = "SHA256";
        openssl_sign($content, $binary_signature, $privateKey, $algo);
        $sign = base64_encode($binary_signature);
        return $sign;
    }

    /* PHP CURL HTTPS POST */
    public static function curl_post_https($url, $data, $header)
    { // 模拟提交数据函数
        $curl = curl_init(); // 启动一个CURL会话
        curl_setopt($curl, CURLOPT_URL, $url); // 要访问的地址
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); // 对认证证书来源的检查
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0); // 从证书中检查SSL加密算法是否存在，如果出错则修改为0，默认为1
        curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // 模拟用户使用的浏览器
        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); // 使用自动跳转
        curl_setopt($curl, CURLOPT_AUTOREFERER, 1); // 自动设置Referer
        curl_setopt($curl, CURLOPT_POST, 1); // 发送一个常规的Post请求
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data); // Post提交的数据包
        curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 设置超时限制防止死循环
        curl_setopt($curl, CURLOPT_HEADER, 0); // 显示返回的Header区域内容
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 获取的信息以文件流的形式返回
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
        $tmpInfo = curl_exec($curl); // 执行操作
        if (curl_errno($curl)) {
            echo 'Errno' . curl_error($curl); //捕抓异常
        }
        curl_close($curl); // 关闭CURL会话
        return $tmpInfo; // 返回数据，json格式
    }
    public static function getTransferBill($url, $header)
    {
        $ch = curl_init();

// 设置cURL选项
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 根据需要启用SSL证书验证
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);


// 执行cURL请求
        $response = curl_exec($ch);
        if(curl_errno($ch)){
            throw new \Exception(curl_error($ch));
        }
        curl_close($ch);
        if(empty($response)){
            throw new \Exception('请求失败');
        }
        if(is_string($response)){
            $response = json_decode($response,true);
        }
        return $response;
    }
    public static function curl_get_https($url, $header)
    {
        $curl = curl_init(); // 启动一个CURL会话
        curl_setopt($curl, CURLOPT_URL, $url); // 要访问的地址
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); // 对认证证书来源的检查
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0); // 从证书中检查SSL加密算法是否存在，如果出错则修改为0，默认为1
        curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // 模拟用户使用的浏览器
        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); // 使用自动跳转
        curl_setopt($curl, CURLOPT_AUTOREFERER, 1); // 自动设置Referer
        curl_setopt($curl, CURLOPT_POST, 0); // 发送一个常规的Post请求
//        curl_setopt($curl, CURLOPT_POSTFIELDS, $data); // Post提交的数据包
        curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 设置超时限制防止死循环
        curl_setopt($curl, CURLOPT_HEADER, 0); // 显示返回的Header区域内容
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 获取的信息以文件流的形式返回
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
        $tmpInfo = curl_exec($curl); // 执行操作
//        echo "<pre>";
//        var_dump($tmpInfo);
//        exit;
        if (curl_errno($curl)) {
            echo 'Errno' . curl_error($curl); //捕抓异常
        }
        curl_close($curl); // 关闭CURL会话
        return $tmpInfo; // 返回数据，json格式
    }

    public static function decryptToString($associatedData, $nonceStr, $ciphertext, $aesKey)
    {
        $ciphertext = base64_decode($ciphertext);
        if (strlen($ciphertext) <= self::AUTH_TAG_LENGTH_BYTE) {
            return false;
        }
        if (function_exists('\sodium_crypto_aead_aes256gcm_is_available') && \sodium_crypto_aead_aes256gcm_is_available()) {
            $strRes = sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $aesKey);
            return $strRes;
        }
        if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') && \Sodium\crypto_aead_aes256gcm_is_available()) {
            $strRes = \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $aesKey);
            return $strRes;
        }
        if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) {
            $ctext = substr($ciphertext, 0, -self::AUTH_TAG_LENGTH_BYTE);
            $authTag = substr($ciphertext, -self::AUTH_TAG_LENGTH_BYTE);
            $strRes =  \openssl_decrypt($ctext, 'aes-256-gcm', $aesKey, \OPENSSL_RAW_DATA, $nonceStr,
                $authTag, $associatedData);
            return $strRes;
        }
        throw new \RuntimeException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php');

    }
    public static function decryptToJson($associatedData, $nonceStr, $ciphertext, $aesKey)
    {
        $res = self::decryptToString($associatedData, $nonceStr, $ciphertext, $aesKey);
        try {
            $json = json_decode($res, true);
            return $json;
        } catch (\Exception $e) {
            return false;
        }
    }
    public static function pay($out_trade_no, $total_fee, $body, $openid='', $notify_url='',$type='mini')
    {
        if(empty(self::$config)){
            throw new \Exception('请先设置微信支付参数');
        }
        if(empty(self::$config['appid'])){
            throw new \Exception('请先设置微信支付参数appid');
        }
        if(empty(self::$config['mchid'])){
            throw new \Exception('请先设置微信支付参数mchid');
        }
        if(empty(self::$config['serial_no'])){
            throw new \Exception('请先设置微信支付参数serial_no');
        }
        if(empty(self::$config['private_key'])&&empty(self::$config['private_key_path'])){
            throw new \Exception('请先设置微信支付参数private_key');
        }
        if($type=='mini'){
            $url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
        }elseif ($type=='app'){
            $url = "https://api.mch.weixin.qq.com/v3/pay/transactions/app";
        }elseif ($type=='h5'){
            $url = "https://api.mch.weixin.qq.com/v3/pay/transactions/h5";
        }elseif ($type=='official'){
            $url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
        }
        $urlarr = parse_url($url);
        $appid = self::$config['appid']; //appID
        $mchid = self::$config['mchid']; //商户ID
        $xlid = self::$config['serial_no']; //证书序列号
        $time = time(); //时间戳
        $noncestr = self::getNonceStr();
        $data = array();
        $data['appid'] = $appid;
        $data['mchid'] = $mchid;
        $data['description'] = $body;
        $data['out_trade_no'] = $out_trade_no;
        $data['notify_url'] = $notify_url;
        $data['amount']['total'] = $total_fee;
        if($type=='h5'){
            $data['scene_info']['h5_info']['type'] = 'wap';
        }elseif ($type=='mini'||$type=='official'){
            $data['payer']['openid'] = $openid; //openid
        }
//        $data['payer']['openid'] = $openid;
        $data['scene_info']['payer_client_ip'] = $_SERVER["REMOTE_ADDR"];; //场景ip
        $data = json_encode($data);
        $key = self::getSign($data, $urlarr['path'], $noncestr, $time);
//        echo '<pre>';
//        print_r($key);
//        exit;
        $token = sprintf('mchid="%s",serial_no="%s",nonce_str="%s",timestamp="%d",signature="%s"', $mchid, $xlid, $noncestr, $time, $key);
        //头部信息
        $header = array(
            'Accept: application/json',
            'Content-Type: application/json',
            'User-Agent:*/*',
            'Authorization: WECHATPAY2-SHA256-RSA2048 ' . $token
        );
        $res = self::curl_post_https($url, $data, $header);
        if(is_string($res)){
            $res = json_decode($res,true);
        }
        if($type=='h5'){
            if(empty($res['h5_url'])){
                throw new \Exception('微信支付失败');
            }
            return $res;
        }else{
            if(empty($res['prepay_id'])){
                throw new \Exception('微信支付失败');
            }
            $prepay_id = $res['prepay_id'];
//            $paySign = self::getWechartSign($appid, $time, $noncestr, $prepay_id);
            if($type=='mini'){
                $paySign = self::getWechartSign($appid, $time, $noncestr, 'prepay_id=' . $prepay_id);
                $payData = [
                    'timeStamp' => (string)$time,
                    'nonceStr' => $noncestr,
                    'package' => 'prepay_id=' . $prepay_id,
                    'signType' => 'RSA',
                    'paySign' => $paySign
                ];
            }elseif ($type=='app'){
                $paySign = self::getWechartSign($appid, $time, $noncestr, $prepay_id);
                $payData = [
                    'appid' => $appid,
                    'partnerid' => $mchid,
                    'prepayid' => $prepay_id,
                    'package' => "Sign=WXPay",
                    'noncestr' => $noncestr,
                    'timestamp' => (string)$time,
                    'sign' => $paySign
                ];
            }elseif ($type=='official'){
                $paySign = self::getWechartSign($appid, $time, $noncestr, 'prepay_id=' . $prepay_id);
                $payData = [
                    'appId'=>$appid,
                    'timeStamp' => (string)$time,
                    'nonceStr' => $noncestr,
                    'package' => 'prepay_id=' . $prepay_id,
                    'signType' => 'RSA',
                    'paySign' => $paySign
                ];
            }

            return $payData;
        }
    }

}
